package com.krautzlabs;

import java.util.HashMap;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;

public class MapView extends SurfaceView implements SurfaceHolder.Callback{
	
	//private DrawThread _mapDraw;
	private Main mainloop;
	private Bitmap _map;  
	private int mapHeight;
	private int mapWidth;
	private int screenHeight;
	private int screenWidth;
	private float aspectRatio;
	private float minZoom;
	private final float maxZoom = 3f;
	private boolean maxZoomOut;
	private boolean maxZoomIn;
	private long millis = (long) 0.0;
	private int minFPS = 0;
	private int maxFPS = 0;
	
	private Paint paint;
	
	Matrix matrix = new Matrix();
    Matrix savedMatrix = new Matrix();
    Matrix tempMatrix = new Matrix();
    
    private RectF bounds;

    // We can be in one of these 3 states
    static final int NONE = 0;
    static final int SELECT = 1;
    static final int ZOOM = 2;
    private int mode = NONE;

    // Remember some things for zooming
    private RectF selection = new RectF();
    private Paint selectPaint = new Paint();
    private PointF start = new PointF();
    private PointF mid = new PointF();
    private float oldDist = 1f;
    private float[] values = new float[9];
    private boolean drawSelectionBox;
    
    private HashMap<String,Bitmap> images;
    
	
	public MapView(Context context, int w, int h) {
		super(context);
		
		_map = BitmapFactory.decodeResource(getResources(), R.drawable.test);		
		mapHeight = _map.getHeight();
		mapWidth = _map.getWidth();
		
		bounds = new RectF();
		
		images = new HashMap<String,Bitmap>();
		
		images.put("tank", BitmapFactory.decodeResource(getResources(), R.drawable.tank));
				
		screenWidth = w;
		screenHeight = h;
		aspectRatio = (float)(screenWidth)/(float)(screenHeight);
		//calculate initial zoom level
		float mapAspectR = (float)(mapWidth)/(float)(mapHeight);
		//limit outer zoom to width
		if(mapAspectR < aspectRatio){
			minZoom = (float)(screenWidth)/(float)(mapWidth);			
		}
		//limit outer zoom to the height
		else{
			minZoom = (float)(screenHeight)/(float)(mapHeight);
		}
		if(matrix.isIdentity()){
			matrix.set(savedMatrix);
			matrix.postScale(minZoom, minZoom);
		}
		maxZoomOut = true;
		drawSelectionBox = false;
		selectPaint.setStyle(Paint.Style.STROKE);
		selectPaint.setColor(Color.rgb(164, 198, 57));
		selectPaint.setStrokeWidth(2f);
		DashPathEffect dashPath = new DashPathEffect(new float[]{10,5}, 0);
		selectPaint.setPathEffect(dashPath);
		
		getHolder().addCallback(this);
		paint = new Paint();
		
		//_mapDraw = new DrawThread(getHolder(), this);
		this.mainloop = new Main(context, getHolder(), this);
		
		setFocusable(true);
		setOnTouchListener(new OnTouchListener() {
			public boolean onTouch(View v, MotionEvent event) {
		        // Handle touch events here...
		        switch (event.getAction() & MotionEvent.ACTION_MASK) {
		        case MotionEvent.ACTION_DOWN:
		            savedMatrix.set(matrix);
		            start.set(event.getX(), event.getY());
		            Log.d("Touch", "mode=DRAG");
		            mode = SELECT;
		            break;
		        case MotionEvent.ACTION_POINTER_DOWN:
		            oldDist = spacing(event);
		            Log.d("Touch", "oldDist=" + oldDist);
		            if (oldDist > 10f) {
		                savedMatrix.set(matrix);
		                midPoint(mid, event);
		                mode = ZOOM;
		                Log.d("Touch", "mode=ZOOM");
		            }
		            drawSelectionBox = false;
		            break;
		        case MotionEvent.ACTION_UP:
		        case MotionEvent.ACTION_POINTER_UP:
		            mode = NONE;
		            Log.d("Touch", "mode=NONE");
		            drawSelectionBox = false;
		            break;
		        case MotionEvent.ACTION_MOVE:
		            if (mode == SELECT) {
		            	drawSelectionBox = true;
		            	float x = event.getX();
		            	float y = event.getY();
		            	//current point is left of start
		            	if(x < start.x){
		            		//start is the bottom right
		            		if(y < start.y){
		            			selection.set(x, y, start.x, start.y);
		            		}
		            		//start is the top right
		            		else{
		            			selection.set(x, start.y, start.x, y);
		            		}
		            	}
		            	else{
		            		//start is the bottom left
		            		if(y < start.y){
		            			selection.set(start.x, y, x, start.y);
		            		}
		            		//start is the top left
		            		else{
		            			selection.set(start.x, start.y, x, y);
		            		}
		            	}
		            } else if (mode == ZOOM) {
		                float newDist = spacing(event);
		                if (newDist > 10f) {
		                    float scale = newDist / oldDist;
		                    //check to see if we are full zoomed out and trying to zoom out more
		                    if((scale > 1.0 || !maxZoomOut) || (scale < 1.0 || !maxZoomIn)){
		                    	//this can only be set if we are going to be scaling
		                    	matrix.set(savedMatrix);
		                    	tempMatrix.set(savedMatrix);
			                    matrix.getValues(values);
		                    	maxZoomOut = false;
		                    	maxZoomIn = false;
		                    	PointF newMid = new PointF();
		                    	midPoint(newMid, event);
		                    	//case that we are going to zoom too far out
		                    	if(values[Matrix.MSCALE_X] * scale < minZoom){
		                    		maxZoomOut = true;
		                    		scale = minZoom/values[Matrix.MSCALE_X];
		                    	}
		                    	else if(values[Matrix.MSCALE_X] * scale > maxZoom){
		                    		maxZoomIn = true;
		                    		scale = maxZoom/values[Matrix.MSCALE_X];
		                    	}		                    	
		                    	tempMatrix.postScale(scale, scale, mid.x, mid.y);
		                    	checkForTranslate(tempMatrix,(newMid.x-mid.x),(newMid.y-mid.y),scale);
		                    }
		                }
		            }
		            break;
		        }
		        
		        return true; // indicate event was handled
		    }
		});
	}
 
	private float spacing(MotionEvent event) {
		float x = event.getX(0) - event.getX(1);
		float y = event.getY(0) - event.getY(1);
		return FloatMath.sqrt(x * x + y * y);
	}
	
	private void midPoint(PointF point, MotionEvent event) {
        float x = event.getX(0) + event.getX(1);
        float y = event.getY(0) + event.getY(1);
        point.set(x / 2, y / 2);
    }
	
	private void checkForTranslate(Matrix m, float transX, float transY, float scale){
		m.getValues(values);
		//calculate the new size of the bitmap being drawn
		float newWidth = values[Matrix.MSCALE_X] * mapWidth;
		float newHeight = values[Matrix.MSCALE_Y] * mapHeight;
		//check if we have moved too far right
		if(values[Matrix.MTRANS_X] + transX > 0){
			transX = -values[Matrix.MTRANS_X];
		}
		//check if we have gone too far left
		else if(values[Matrix.MTRANS_X] + transX + newWidth < screenWidth){
			transX = screenWidth-(values[Matrix.MTRANS_X]+newWidth);
		}
		//check if we have gone too far down
		if(values[Matrix.MTRANS_Y] + transY > 0){
			transY = -values[Matrix.MTRANS_Y];
		}
		//check if we have gone too far up
		else if(values[Matrix.MTRANS_Y] + transY + newHeight < screenHeight){
			transY = screenHeight-(values[Matrix.MTRANS_Y]+newHeight);
		}
		if(scale != 0f){
			matrix.postScale(scale, scale, mid.x, mid.y);
		}
		matrix.postTranslate(transX, transY);
	}
	
	protected void onDraw(Canvas canvas){
		if(canvas!=null){
			int FPS = 0;
			if(millis == 0.0){
				millis = System.currentTimeMillis();
			}
			else{
				long time = System.currentTimeMillis();
				FPS = (int) (1000.0/(time-millis));
				if(minFPS==0 || FPS < minFPS){
					minFPS = FPS;
				}
				if(maxFPS==0 || FPS > maxFPS){
					maxFPS = FPS;
				}
				millis = time;
			}
			
			canvas.drawColor(Color.BLACK);
			canvas.drawBitmap(_map, matrix, null);
			
			paint.setColor(Color.WHITE);
			//draw map points
			if(mainloop != null)
			{
				for(MapPoint mp:mainloop.getGameState().getMapPoints())
				{
					bounds.set(mp.getX(), mp.getY()+20, mp.getX()+20, mp.getY());
					Bitmap temp = images.get("tank");
					
					matrix.mapRect(bounds);
					
					canvas.drawBitmap(temp, null, bounds, null);
					
					//look to see if its a base point
					if(mp instanceof BasePoint)
					{
						//if its allied draw it green, axis=red. basepoints are determined by the side attribute in the xml file
						//players are also temporarily created in xml parser. Bases hold instances of players
						BasePoint bp = (BasePoint)mp;
						if(bp.getOwner().getPlayerSide() == Player.PlayerSide.ALLIED)
						{
							paint.setColor(Color.GREEN);
						}
						else
						{
							paint.setColor(Color.RED);
						}
					}
					else
					{
						paint.setColor(Color.WHITE);
					}
					//draw map circle
					//canvas.drawCircle(points[0], points[1], (points[2]-points[0]), paint);
					
					//just for fun, draw lines to neighbors
					for(MapPoint neighbor: mp.getNeighbors())
					{
						float[] npoints = new float[4];
						npoints[0] = neighbor.getX();
						npoints[1] = neighbor.getY();
						npoints[2] = mp.getX();
						npoints[3] = mp.getY();
						matrix.mapPoints(npoints);
						canvas.drawLine(npoints[2], npoints[3], npoints[0], npoints[1], paint);
					}
				}
			}
			if(drawSelectionBox){
				canvas.drawRoundRect(selection, 3f, 3f, selectPaint);
			}
			paint.setTextSize(18);
			canvas.drawText("MIN    : "+Integer.toString(minFPS), 5, 15, paint);
			canvas.drawText("CURRENT: "+Integer.toString(FPS), 5, 30, paint);
			canvas.drawText("MAX    : "+Integer.toString(maxFPS), 5, 45, paint);
		}
	}

	public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
		// TODO Auto-generated method stub
		
	}

	public void surfaceCreated(SurfaceHolder holder) {
		//_mapDraw.setRunning(true);
		//_mapDraw.start();
		this.mainloop.setRunning(true);
		this.mainloop.start();
	}

	public void surfaceDestroyed(SurfaceHolder holder) {
		// TODO Auto-generated method stub
		
	}

}
